/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "driver/rmt_tx.h"
#include "driver/rmt_rx.h"
#include "rcs_api.h"

/**
 * @brief NEC timing spec
 */
#define NEC_LEADING_CODE_DURATION_0 9000
#define NEC_LEADING_CODE_DURATION_1 4500
#define NEC_PAYLOAD_ZERO_DURATION_0 560
#define NEC_PAYLOAD_ZERO_DURATION_1 560
#define NEC_PAYLOAD_ONE_DURATION_0 560
#define NEC_PAYLOAD_ONE_DURATION_1 1690
#define NEC_REPEAT_CODE_DURATION_0 9000
#define NEC_REPEAT_CODE_DURATION_1 2250
#define NEC_DECODE_MARGIN 200          // Tolerance for parsing RMT symbols into bit stream
#define NEC_DECODE_LEADING_MARGIN 1000 // Tolerance for parsing RMT symbols into bit stream

static const char *TAG = "rmt";

/**
 * @brief Check whether a duration is within expected range
 */
static inline bool nec_check_in_range(uint32_t signal_duration, uint32_t spec_duration)
{
    return (signal_duration < (spec_duration + NEC_DECODE_MARGIN)) &&
           (signal_duration > (spec_duration - NEC_DECODE_MARGIN));
}

static inline bool nec_check_leading_in_range(uint32_t signal_duration, uint32_t spec_duration)
{
    return (signal_duration < (spec_duration + NEC_DECODE_LEADING_MARGIN)) &&
           (signal_duration > (spec_duration - NEC_DECODE_LEADING_MARGIN));
}

/**
 * @brief Check whether a RMT symbol represents NEC logic zero
 */
static bool nec_parse_logic0(rmt_symbol_word_t *rmt_nec_symbols)
{
    return nec_check_in_range(rmt_nec_symbols->duration0, NEC_PAYLOAD_ZERO_DURATION_0) &&
           nec_check_in_range(rmt_nec_symbols->duration1, NEC_PAYLOAD_ZERO_DURATION_1);
}

/**
 * @brief Check whether a RMT symbol represents NEC logic one
 */
static bool nec_parse_logic1(rmt_symbol_word_t *rmt_nec_symbols)
{
    return nec_check_in_range(rmt_nec_symbols->duration0, NEC_PAYLOAD_ONE_DURATION_0) &&
           nec_check_in_range(rmt_nec_symbols->duration1, NEC_PAYLOAD_ONE_DURATION_1);
}

/**
 * @brief Check whether a duration is within expected range
 */
static inline bool rcs_check_in_range(uint32_t signal_duration, uint32_t spec_duration)
{
    return (signal_duration < (spec_duration + CUSTOM_DECODE_MARGIN)) &&
           (signal_duration > (spec_duration - CUSTOM_DECODE_MARGIN));
}

static inline bool rcs_check_leading_in_range(uint32_t signal_duration, uint32_t spec_duration)
{
    return (signal_duration < (spec_duration + CUSTOM_DECODE_LEADING_MARGIN)) &&
           (signal_duration > (spec_duration - CUSTOM_DECODE_LEADING_MARGIN));
}

/**
 * @brief Check whether a RMT symbol represents NEC logic zero
 */
static bool rcs_parse_logic0(rmt_symbol_word_t *rmt_nec_symbols)
{
    return rcs_check_in_range(rmt_nec_symbols->duration0, CUSTOM_PAYLOAD_ZERO_DURATION_0) &&
           rcs_check_in_range(rmt_nec_symbols->duration1, CUSTOM_PAYLOAD_ZERO_DURATION_1);
}

/**
 * @brief Check whether a RMT symbol represents NEC logic one
 */
static bool rcs_parse_logic1(rmt_symbol_word_t *rmt_nec_symbols)
{
    return rcs_check_in_range(rmt_nec_symbols->duration0, CUSTOM_PAYLOAD_ONE_DURATION_0) &&
           rcs_check_in_range(rmt_nec_symbols->duration1, CUSTOM_PAYLOAD_ONE_DURATION_1);
}

/**
 * @brief Decode RMT symbols into NEC address and command
 */
bool nec_parse_frame(rmt_symbol_word_t *rmt_nec_symbols, uint8_t *command)
{
    if (!command)
    {
        return false;
    }
    else
    {
        *command = 0;
    }
    rmt_symbol_word_t *cur = rmt_nec_symbols;
    uint16_t whole_address = 0;
    uint16_t whole_command = 0;
    bool valid_leading_code = nec_check_leading_in_range(cur->duration0, NEC_LEADING_CODE_DURATION_0) &&
                              nec_check_leading_in_range(cur->duration1, NEC_LEADING_CODE_DURATION_1);
    if (!valid_leading_code)
    {
        return false;
    }
    cur++;
    for (int i = 0; i < 16; i++)
    {
        if (nec_parse_logic1(cur))
        {
            whole_address |= 1 << i;
        }
        else if (nec_parse_logic0(cur))
        {
            whole_address &= ~(1 << i);
        }
        else
        {
            return false;
        }
        cur++;
    }
    for (int i = 0; i < 16; i++)
    {
        if (nec_parse_logic1(cur))
        {
            whole_command |= 1 << i;
        }
        else if (nec_parse_logic0(cur))
        {
            whole_command &= ~(1 << i);
        }
        else
        {
            return false;
        }
        cur++;
    }
    // check
    uint8_t high_byte = 0;
    uint8_t low_byte = 0;
    low_byte = (whole_address & 0x00FF);
    high_byte = (whole_address & 0xFF00) >> 8;
    if ((uint8_t)(~high_byte) != (uint8_t)low_byte)
    {
        ESP_LOGE(TAG, "address error: %04x, low_byte:%d, high_byte:%d, ~high_byte:%02x", whole_address, low_byte, high_byte, ~high_byte);
        // return false;
    }
    low_byte = (whole_command & 0x00FF);
    high_byte = (whole_command & 0xFF00) >> 8;
    if ((uint8_t)(~high_byte) != (uint8_t)low_byte)
    {
        ESP_LOGE(TAG, "command error: %04x", whole_command);
        // return false;
    }
    ESP_LOGE(TAG, "command error: %04x, low_byte:%d, high_byte:%d, ~high_byte:%02x", whole_command, low_byte, high_byte, ~high_byte);
    // ESP_LOGE(TAG, "command: %02d %02d", high_byte, low_byte);
    *command = low_byte;
    return true;
}

bool custom_parse_frame(rmt_symbol_word_t *rmt_nec_symbols, uint8_t *dummy_array)
{
    if (!dummy_array)
    {
        return false;
    }
    ESP_LOGI(TAG, "lead check.");
    rmt_symbol_word_t *cur = rmt_nec_symbols;
    bool valid_leading_code = rcs_check_leading_in_range(cur->duration0, CUSTOM_LEADING_CODE_DURATION_0) &&
                              rcs_check_leading_in_range(cur->duration1, CUSTOM_LEADING_CODE_DURATION_1);
    if (!valid_leading_code)
    {
        return false;
    }
    cur++;
    ESP_LOGI(TAG, "lead valid.");
    // BULLET_DATA_LEN = 5
    for (size_t j = 0; j < 5; j++)
    {
        for (size_t i = 0; i < 8; i++)
        {
            if (rcs_parse_logic1(cur))
            {
                dummy_array[j] |= 1 << i;
            }
            else if (rcs_parse_logic0(cur))
            {
                dummy_array[j] &= ~(1 << i);
            }
            else
            {
                ESP_LOGE(TAG, "symbol[%d] error: 1: %d. 0: %d", j * 8 + i, cur->duration1, cur->duration0);
                return false;
            }
            cur++;
        }
    }
    return true;
}

/**
 * @brief Check whether the RMT symbols represent NEC repeat code
 */
bool nec_parse_frame_repeat(rmt_symbol_word_t *rmt_nec_symbols)
{
    return nec_check_in_range(rmt_nec_symbols->duration0, NEC_REPEAT_CODE_DURATION_0) &&
           nec_check_in_range(rmt_nec_symbols->duration1, NEC_REPEAT_CODE_DURATION_1);
}

bool fix_bullet_parse_frame(rmt_symbol_word_t *rmt_nec_symbols, uint8_t *dummy_array)
{
    if (!dummy_array)
    {
        return false;
    }
    ESP_LOGI(TAG, "fix lead check.");
    rmt_symbol_word_t *cur = rmt_nec_symbols;
    bool valid_leading_code = rcs_check_leading_in_range(cur->duration0, CUSTOM_LEADING_CODE_DURATION_0) &&
                              rcs_check_leading_in_range(cur->duration1, CUSTOM_LEADING_CODE_DURATION_1);
    if (!valid_leading_code)
    {
        return false;
    }
    cur++;
    ESP_LOGI(TAG, "fix lead valid.");
    for (size_t i = 0; i < 8; i++)
    {
        if (rcs_parse_logic1(cur))
        {
            dummy_array[0] |= 1 << i;
        }
        else if (rcs_parse_logic0(cur))
        {
            dummy_array[0] &= ~(1 << i);
        }
        else
        {
            ESP_LOGE(TAG, "fix symbol[%d] error: 1: %d. 0: %d", i, cur->duration1, cur->duration0);
            return false;
        }
        cur++;
    }
    return true;
}
